#include "Servidor.h"

Servidor::Servidor(string archivo,char letra ) {
	if(this->salas.crear((char*)"/bin/rm", 'l') != SHM_OK)
		perror ("error al crear memoria compartida");
	if(this->usuarios.crear((char*)"/bin/mkdir", 'u') != SHM_OK)
		perror ("error al crear memoria compartida");
	Usuarios usuarios = this->usuarios.leer();
	usuarios.size = 0;
	usuarios.libres = 0;
	this->usuarios.escribir(usuarios);
	Salas salas = this->salas.leer();
	salas.size = 0;
	salas.libres = 0;
	this->salas.escribir(salas);
	this->colaMensajeSalas = new Cola<MensajeSalas> (archivo.c_str(), letra);
	this->colaMensajeUsuarios = new Cola<MensajeUsuarios> (archivo.c_str(), letra + 1);
	this->colaMensajeNuevoCliente = new Cola<MensajeNuevoCliente> (archivo.c_str(), letra + 2);
	this->colaMensajeConectarASala = new Cola<MensajeConectarASala> (archivo.c_str(), letra + 3);
	this->colaMensajeNuevoMensaje = new Cola<MensajeNuevoMensaje> (archivo.c_str(), letra + 4);
}

Servidor::~Servidor() {
	this->salas.liberar();
	this->usuarios.liberar();
	this->colaMensajeSalas->destruir();
	delete this->colaMensajeSalas;
	this->colaMensajeUsuarios->destruir();
	delete this->colaMensajeUsuarios;
	this->colaMensajeNuevoCliente->destruir();
	delete this->colaMensajeNuevoCliente;
	this->colaMensajeConectarASala->destruir();
	delete this->colaMensajeConectarASala;
	this->colaMensajeNuevoMensaje->destruir();
	delete this->colaMensajeNuevoMensaje;
}

void Servidor::atenderPeticionListaDeSalas() {
	MensajeSalas peticion;
	if (this->colaMensajeSalas->leer(PETICION, &peticion) == sizeof(MensajeSalas) - sizeof(long)) {
		MensajeSalas respuesta;
		Salas salas = this->salas.leer();
		for(int i = 0; i < salas.size; i++) {
			Sala sala = salas.sala[i];
			if(sala.usuariosConectados > 0) {
				//Sumo el id para que cada cliente lea la respuesta que le corresponde
				respuesta.mtype = RESPUESTA + peticion.idUsuario;
				respuesta.idUsuario = i;
				string tmp = sala.nombre;
				tmp += " (";
				char num[8];
				sprintf(num, "%d",sala.usuariosConectados);
				tmp += num;
				tmp += " usuarios conectados)";
				strcpy(respuesta.nombreSala, tmp.c_str());
				this->colaMensajeSalas->escribir(respuesta);
			}
		}
		//Sumo el id para que cada cliente lea la respuesta que le corresponde
		respuesta.mtype = RESPUESTA + peticion.idUsuario;
		respuesta.idUsuario = -1;
		strcpy(respuesta.nombreSala, "");
		this->colaMensajeSalas->escribir(respuesta);
	}
}

void Servidor::atenderPeticionListaDeUsuarios() {
	MensajeUsuarios peticion;
	if (this->colaMensajeUsuarios->leer(PETICION, &peticion) == sizeof(MensajeUsuarios) - sizeof(long)) {
		MensajeUsuarios respuesta;
		Usuarios usuarios = this->usuarios.leer();
		for(int i = 0; i < usuarios.size; i++) {
			Usuario usuario = usuarios.usuario[i];
			cout<<"id sala "<<peticion.idSala<<endl;
			if((usuario.idSala == peticion.idSala - 1)&&(usuario.conectado)) {
				cout<<"escribiendo id "<<RESPUESTA + peticion.idUsuario<<endl;
				//Sumo el id para que cada cliente lea la respuesta que le corresponde
				respuesta.mtype = RESPUESTA + peticion.idUsuario;
				respuesta.idUsuario = i + 1;
				strcpy(respuesta.nombreUsuario, usuario.apodo);
				cout<<"id usuario "<<respuesta.idUsuario<<endl;
				cout<<"nombre usuario "<<respuesta.nombreUsuario<<endl;
				this->colaMensajeUsuarios->escribir(respuesta);
			}
		}
		//Sumo el id para que cada cliente lea la respuesta que le corresponde
		respuesta.mtype = RESPUESTA + peticion.idUsuario;
		respuesta.idUsuario = -1;
		strcpy(respuesta.nombreUsuario, "");
		this->colaMensajeUsuarios->escribir(respuesta);
	}
}

void Servidor::atenderPeticionClienteNuevo() {
	MensajeNuevoCliente peticion;
	if (this->colaMensajeNuevoCliente->leer(PETICION, &peticion) == sizeof(MensajeNuevoCliente) - sizeof(long)) {
		MensajeNuevoCliente respuesta;
		Usuarios usuarios = this->usuarios.leer();
		if(peticion.id==-1) {
			//Mientras no haya espacio para un nuevo usuario, espero a que se desconecte alguno...
			while((usuarios.libres == 0)&&(usuarios.size == MAX_CLIENTES)) {
				usuarios = this->usuarios.leer();
			}
			int idNuevoUsuario;
			//Si hay usuarios que se desconectaron, puedo usar un id libre
			if(usuarios.libres > 0) {
				for(int i = 0; i < usuarios.size; i++) {
					Usuario aux = usuarios.usuario[i];
					if(!aux.conectado) {
						idNuevoUsuario = i;
						usuarios.libres--;
						break;
					}
				}
			}
			else {
				//Sino, agrego un cliente al vector
				idNuevoUsuario = usuarios.size;
				usuarios.size++;
			}
			strcpy(usuarios.usuario[idNuevoUsuario].apodo, "");
			usuarios.usuario[idNuevoUsuario].idSala = -1;
			this->usuarios.escribir(usuarios);
			respuesta.mtype = RESPUESTA;
			respuesta.id = idNuevoUsuario + 1;
			//Asigno un id al cliente y lo envio
			this->colaMensajeNuevoCliente->escribir(respuesta);
		} else {
			//Una vez que el cliente conoce su id, lo envía junto con su apodo
			//Verifico que el apodo no este en uso
			for(int i = 0; i < usuarios.size; i++) {
				if((usuarios.usuario[i].conectado)&&(strcmp(peticion.apodo, usuarios.usuario[i].apodo) == 0))
				{
					//Si ya existe, aviso al cliente seteando el id en -1
					respuesta.mtype = RESPUESTA + peticion.id;
					respuesta.id = -1;
					this->colaMensajeNuevoCliente->escribir(respuesta);
					return;
				}
			}
			//Si no existe lo asigno al id
			respuesta.mtype = RESPUESTA + peticion.id;
			respuesta.id = peticion.id;
			this->colaMensajeNuevoCliente->escribir(respuesta);
			strcpy(usuarios.usuario[peticion.id - 1].apodo, peticion.apodo);
			usuarios.usuario[peticion.id - 1].conectado = true;
			this->usuarios.escribir(usuarios);
		}
	}
}

void Servidor::atenderPeticionConectarASala()
{
	MensajeConectarASala peticion;
	if (this->colaMensajeConectarASala->leer(PETICION, &peticion) == sizeof(MensajeConectarASala) - sizeof(long)) {
		MensajeConectarASala respuesta;
		respuesta.mtype = RESPUESTA + peticion.idUsuario;
		Salas salas = this->salas.leer();
		if(peticion.idSala == 0) {
			//Verifico que la sala esté en uso
			for(int i = 0; i < salas.size; i++) {
				if((salas.sala[i].usuariosConectados>0)&&(strcmp(salas.sala[i].nombre, peticion.nombreSala) == 0)) {
					respuesta.idSala = -1;
					this->colaMensajeConectarASala->escribir(respuesta);
					return;
				}
			}
			//Mientras no haya espacio para una sala nueva, espero a que se libere alguna...
			while((salas.libres == 0)&&(salas.size == MAX_SALAS)) {
				salas = this->salas.leer();
			}
			int idNuevaSala;
			//Si hay salas libres, puedo usar un id libre
			if(salas.libres > 0) {
				for(int i = 0; i < salas.size; i++) {
					Sala aux = salas.sala[i];
					if(aux.usuariosConectados == 0) {
						idNuevaSala = i;
						salas.libres--;
						break;
					}
				}
			}
			else {
				//Sino, agrego una sala al vector
				idNuevaSala = salas.size;
				salas.size++;
			}
			strcpy(salas.sala[idNuevaSala].nombre, peticion.nombreSala);
			salas.sala[idNuevaSala].usuariosConectados = 0;
			peticion.idSala = idNuevaSala + 1;
		}
		//Asigno la sala al cliente
		Usuarios usuarios = this->usuarios.leer();
		usuarios.usuario[peticion.idUsuario - 1].idSala = peticion.idSala - 1;
		this->usuarios.escribir(usuarios);
		//Sumo 1 a usuarios conectados
		salas.sala[peticion.idSala - 1].usuariosConectados++;
		this->salas.escribir(salas);
		respuesta.idSala = peticion.idSala;
		this->colaMensajeConectarASala->escribir(respuesta);
		//Envio el mensaje de conexión a la sala
		MensajeNuevoMensaje peticionMsg;
		peticionMsg.mtype = PETICION;
		peticionMsg.idUsuario = peticion.idUsuario;
		peticionMsg.conectoASala = true;
		strcpy(peticionMsg.apodo, usuarios.usuario[peticion.idUsuario - 1].apodo);
		this->colaMensajeNuevoMensaje->escribir(peticionMsg);
	}
}

void Servidor::atenderPeticionMensajeNuevo() {
	MensajeNuevoMensaje peticion;
	if (this->colaMensajeNuevoMensaje->leer(PETICION, &peticion) == sizeof(MensajeNuevoMensaje) - sizeof(long)) {
		Usuarios usuarios = this->usuarios.leer();
		Salas salas = this->salas.leer();
		//Si es un mensaje de desconexion, debo actualizar los vectores
		if(strcmp(peticion.mensaje, "salir") == 0) {
			usuarios.usuario[peticion.idUsuario - 1].conectado = false;
			usuarios.libres++;
			this->usuarios.escribir(usuarios);
			int idSala = usuarios.usuario[peticion.idUsuario - 1].idSala;
			salas.sala[idSala].usuariosConectados--;
			if(salas.sala[idSala].usuariosConectados == 0)
				salas.libres++;
			this->salas.escribir(salas);
		}
		for(int i = 0; i < usuarios.size; i++)
		{
			Usuario aux = usuarios.usuario[i];
			if((i != peticion.idUsuario - 1)&&(aux.conectado)) {
				if(aux.idSala == usuarios.usuario[peticion.idUsuario - 1].idSala) {
					peticion.mtype = RESPUESTA + i + 1;
					this->colaMensajeNuevoMensaje->escribir(peticion);
				}
			}
		}
	}
}

int Servidor::usuariosConectados() {
	Usuarios usuarios = this->usuarios.leer();
	return usuarios.size - usuarios.libres;
}
